package com.acme.chat.client;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Map;
import java.util.Set;

import com.acme.chat.message.ChatMessage;
import com.acme.chat.message.Message;
import com.acme.chat.message.MessageType;
import com.acme.chat.server.Server;

public class TcpProxyServer implements Server, Runnable {
    private static final String ERROR_PORT = "Port must be beetween 0 and 65535!";
    private static final int MAX_PORT_NUMBER = 65535;

    private String host;
    private int port;
    private volatile boolean keepGoing;

    private Socket socket;
    private ObjectOutputStream oStream;
    private ObjectInputStream iStream;

    private ChatClient client;

    public TcpProxyServer(String host, int port) {
        validatePort(port);

        this.host = host;
        this.port = port;
        this.keepGoing = true;
    }

    private void validatePort(int port) {
        if (port < 0 || port > MAX_PORT_NUMBER) {
            throw new IllegalArgumentException(ERROR_PORT);
        }
    }

    public boolean addClient(ChatClient client) {
        boolean connectedToTheServer = false;
        Message message = new ChatMessage(MessageType.ERROR);
        try {
            socket = new Socket(host, port);
            oStream = new ObjectOutputStream(socket.getOutputStream());
            iStream = new ObjectInputStream(socket.getInputStream());

            // write room and name of the client
            oStream.writeObject(client.getName());
            oStream.writeObject(client.getRoom());

            // after client was added to server
            message = (Message) iStream.readObject();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        client.incomingMessage(message);

        if (message.getType() == MessageType.MESSAGE ) {
            (new Thread(this)).start();
            connectedToTheServer = true;
            this.client = client;
        }

        return connectedToTheServer;
    }

    public boolean removeClient(ChatClient client) {
        try {
            oStream.writeObject(new ChatMessage(MessageType.LOGOUT));
        } catch (IOException e) {
            e.printStackTrace();
        }
        client.incomingMessage(new ChatMessage(MessageType.MESSAGE, "You are disconnected from server!\n"));
        this.client = null;
        keepGoing = false;
        close();
        return true;
    }

    public void processMessage(Message message, ChatClient client) {
        try {
            oStream.writeObject(message);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public Set<ChatClient> getClients() {
        return null;
    }

    public Set<String> getRooms() {
        return null;
    }

    public Map<String, Set<ChatClient>> getClientsInTheRoom() {
        return null;
    }

    // Not the server part, need move to another class
    public void run() {
        ChatMessage message;

        while (keepGoing) {
            try {
                message = (ChatMessage) iStream.readObject();
                processMessage(message);
            } catch (IOException e) {
                e.printStackTrace();
                keepGoing = false;
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
                keepGoing = false;
            }
        }
        close();
    }

    private void processMessage(ChatMessage message) {
        if (message.getType() == MessageType.LOGOUT) {
            client.disconnect();
            keepGoing = false;
            return;
        }

        client.incomingMessage(message);
    }

    private void close() {
        try {
            if (oStream != null)
                oStream.close();
        } catch (Exception e) {
        }

        try {
            if (iStream != null)
                iStream.close();
        } catch (Exception e) {
        }

        try {
            if (socket != null)
                socket.close();
        } catch (Exception e) {
        }
    }

}
